#include <assert.h>
#include "rule.h"
#include "stdlib.h"
#include "string.h"
#include "ctype.h"
#include "dictionary.h"
#include "math.h"
#include "stdio.h"
#include "../global/global_declarations.h"


Dictionary* the_dictionary;

/**
 * creates a new dictionary.
 * @return->length = 0.
 * @return->words_arr = NULL.
 */
Dictionary* make_dictionary() {
	Dictionary* d = calloc(1, sizeof(Dictionary));
	d->length = 0;
	d->words_arr = NULL;
	return d;
}

void free_dictionary_record(DictionaryRecord* d) {
	free(d->word.data);
}

void free_dictionary(Dictionary* d) {
	int64 i;
	if (d->length > 0) {
		for (i = d->length - 1; i >= 0; --i) {
			free_dictionary_record(&(d->words_arr[i]));
		}
		d->length = 0;
	}
	free(d->words_arr);
	free(d);
}

/**
 * prints the dictionary to standard output.
 */
void print_dictionary(Dictionary* d) {
	int64 i, l;
	l = d->length;
	printf("word\tindex\n");
	for (i = 0; i < d->length; ++i) {
		String_print(&d->words_arr[i].word);
		printf("\t%ld\n", d->words_arr[i].index);
	}
}

/**
 * returns the dictionary of the program.
 * returns NULL if the dictionary was not created yet.
 */
Dictionary* get_the_dictionary() {
	return the_dictionary;
}

/**
 * creates a new dictionary_record and sets its word and index.
 */
DictionaryRecord make_dictonary_record(String word, int64 index) {
	DictionaryRecord dr;
	dr.word.data = calloc(word.size + 1, 1);
	strcpy(dr.word.data, word.data);
	dr.word.size = word.size;
	dr.index = index;
	return dr;
}

/**
 * returns the number of English letters in the word.
 */
int count_english_letters_in_word(String word) {
	int i, counter;
	counter = 0;
	for (i = 0; i < word.size; ++i) {
		if (is_English_letter(word.data[i]))
			counter++;
	}
	return counter;
}

/**
 * the number of combinations of the words letters as lower/upper case.
 * (2^the length of the word).
 */
int64 num_of_case_insensitive_combinations_word(String word) {
	int64 l, n;
	l = count_english_letters_in_word(word);
	n = pow(2, l);
	return n;
}

/**
 * the total amount of case insensitive words in the dictionary.
 * d must be initialized.
 */
int64 num_of_case_insensitive_words_in_dictionary(Dictionary* d) {
	int64 l, n;
	DictionaryRecord last_record;
	l = d->length;
	if (l == 0)
		return 0;
	last_record = d->words_arr[l - 1];
	n = last_record.index;
	n += num_of_case_insensitive_combinations_word(last_record.word);
	return n;
}

/**
 * inserts at the end of the array
 */
void insert_word_to_dictionary(String word, Dictionary* dict) {
	int64 l, n;
	DictionaryRecord dr = make_dictonary_record(word, 0);
	n = num_of_case_insensitive_words_in_dictionary(dict);
	dict->length++;
	l = dict->length;
	dict->words_arr = realloc(dict->words_arr, l * sizeof(DictionaryRecord));
	dict->words_arr[l - 1] = dr;
	dict->words_arr[l - 1].index = n;
}

/**
 * creates a dictionary out of a given strings fileName.
 */
Dictionary* create_dictionary_from_file(char* fileName) {
	FILE* file;
	Dictionary* d;
	String* s = make_empty_string();
	char str[MAX_LINE_LENGTH] = { 0 };
	d = make_dictionary();
	file = fopen(fileName, "r");
	if (file != NULL) {
		while (fgets(str, MAX_LINE_LENGTH, file) != NULL) {
			remove_end_of_line(str);
			String_copy(s, str);
			insert_word_to_dictionary(*s, d);
			memset(str, 0, MAX_LINE_LENGTH);
			String_reset(s);
		}
		fclose(file);
	} else {
		perror(fileName);
	}
	String_free(s);
	return d;
}

/**
 * compare two dictionary records by there index.
 * if the first is bigger return 1, if the second return -1 and 0 for equality.
 */
int DictionaryRecord_comparator(const void* elem1, const void* elem2) {
	DictionaryRecord *dr1, *dr2;
	int64 i1, i2;
	dr1 = (DictionaryRecord*) elem1;
	dr2 = (DictionaryRecord*) elem2;
	i1 = dr1->index;
	i2 = dr2->index;
	if (i1 < i2)
		return -1;
	if (i1 > i2)
		return 1;
	return 0;
}

/**
 * returns true iff the char is an English letter.
 */
bool is_English_letter(char c){
	return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));
}

